﻿using System;
using System.Xml.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections.Generic;
using Newtonsoft.Json;
using HigLabo.Net.Extensions;
using System.Net;
using HigLabo.Core;

namespace HigLabo.Net.BoxNet
{
    /// <summary>
    /// 
    /// </summary>
    public partial class BoxClient : OAuth2Client
    {
        public static readonly BoxClientDefaultSettings Default = new BoxClientDefaultSettings();

        public BoxClient()
            : this("", "", "")
        {
        }
        public BoxClient(String clientID, String clientSecret, String redirectUrl)
        {
            this.ClientID = clientID;
            this.ClientSecret = clientSecret;
            this.RedirectUrl = redirectUrl;
            this.AccessToken = new OAuth2AccessToken();
            this.AccessToken.SetProperty(Default.AccessToken);
        }

        public override String GetAuthorizeUrl(OAuth2ResponseType responseType)
        {
            Func<String, String> f = HttpClient.UrlEncode;
            return String.Format("https://api.box.com/oauth2/authorize?response_type=code&response_type={0}&client_id={1}&redirect_uri={2}"
                , responseType.ToStringFromEnum().ToLower(), f(this.ClientID), f(this.RedirectUrl));
        }
        protected override HttpRequestCommand CreateGetAccessTokenCommand(String code)
        {
            String url = "https://api.box.com/oauth2/token";
            var cm = new HttpRequestCommand(url);
            cm.MethodName = HttpMethodName.Post;
            var d = new Dictionary<String, String>();
            d["grant_type"] = "authorization_code";
            d["code"] = code;
            d["client_id"] = this.ClientID;
            d["client_secret"] = this.ClientSecret;
            d["redirect_uri"] = this.RedirectUrl;
            cm.SetBodyStream(new HttpBodyFormUrlEncodedData(Encoding.UTF8, d));
            return cm;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        private void GetResponseBodyText(BoxCommand command, Action<String> callback)
        {
            this.GetHttpWebResponse(command, res => callback(res.BodyText));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        private void GetResponseBodyData(BoxCommand command, Action<Byte[]> callback)
        {
            this.GetHttpWebResponse(command, res => callback(res.BodyData));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        private void GetHttpWebResponse(BoxCommand command, Action<HttpResponse> callback)
        {
            var cm = this.CreateHttpRequestCommand(command.MethodName, command.GetBaseUrl(), command.GetParameters());
            if (command.MethodName == HttpMethodName.Post
             || command.MethodName == HttpMethodName.Put
             || command.MethodName == HttpMethodName.Move
             || command.MethodName == HttpMethodName.Copy)
            {
                cm.SetBodyStream(command.Data);
            }
            this.GetResponse(cm, callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="url"></param>
        /// <param name="token"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        private HttpRequestCommand CreateHttpRequestCommand(HttpMethodName methodName, String url, IDictionary<string, string> parameters)
        {
            var cm = new HttpRequestCommand(url);
            cm.MethodName = methodName;
            cm.Headers[HttpRequestHeader.Authorization] = String.Format("Bearer {0}", this.AccessToken.Value);
            foreach (var item in parameters)
            {
                cm.Headers[item.Key] = item.Value;
            }
            return cm;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void CreateFolder(CreateFolderCommand command, Action<Folder> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Folder(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetFolderInfo(GetFolderInfoCommand command, Action<Folder> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Folder(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="folderID"></param>
        /// <param name="callback"></param>
        public void GetFolderInfo(String folderID, Action<Folder> callback)
        {
            var cm = new GetFolderInfoCommand();
            cm.FolderID = folderID;
            this.GetResponseBodyText(cm, s => callback(new Folder(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void RetrieveFolderItems(RetrieveFolderItemsCommand command, Action<ItemCollection> callback)
        {
            this.GetResponseBodyText(command, s => callback(new ItemCollection(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="folderID"></param>
        /// <param name="callback"></param>
        public void RetrieveFolderItems(String folderID, Action<ItemCollection> callback)
        {
            var cm = new RetrieveFolderItemsCommand();
            cm.FolderID = folderID;
            this.GetResponseBodyText(cm, s => callback(new ItemCollection(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void UpdateFolderInfo(UpdateFolderInfoCommand command, Action<Folder> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Folder(s)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetFileInfo(GetFileInfoCommand command, Action<File> callback)
        {
            this.GetResponseBodyText(command, s => callback(new File(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void UpdateFileInfo(UpdateFileInfoCommand command, Action<File> callback)
        {
            this.GetResponseBodyText(command, s => callback(new File(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void CopyFile(CopyFileCommand command, Action<File> callback)
        {
            if (String.IsNullOrEmpty(command.DestinationFolderID)) throw new ArgumentNullException("CopyFileCommand.DestinationFolderID");
            this.GetResponseBodyText(command, s => callback(new File(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void DeleteFile(DeleteFileCommand command, Action<String> callback)
        {
            this.GetResponseBodyText(command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void DownloadFile(DownloadFileCommand command, Action<Byte[]> callback)
        {
            this.GetResponseBodyData(command, callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetCommentsOnFile(GetCommentsOnFileCommand command, Action<CommentCollection> callback)
        {
            this.GetResponseBodyText(command, s => callback(new CommentCollection(s)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"> </param>
        /// <returns></returns>
        public void UploadNewVersion(UploadNewVersionFileCommand command, Action<TemporaryFile> callback)
        {
            this.GetResponseBodyText(command, s => callback(new TemporaryFile(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void AddCommentToFile(AddCommentCommand command, Action<Comment> callback)
        {
            command.AddType = CommentType.Comment;
            this.GetResponseBodyText(command, s => callback(new Comment(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void AddCommentToDiscussion(AddCommentCommand command, Action<Comment> callback)
        {
            command.AddType = CommentType.Discussion;
            this.GetResponseBodyText(command, s => callback(new Comment(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void ChangeCommentMessage(ChangeCommentCommand command, Action<Comment> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Comment(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetCommentInfo(GetCommentCommand command, Action<Comment> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Comment(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void DeleteComment(DeleteCommentCommand command, Action<String> callback)
        {
            this.GetResponseBodyText(command, s => callback(s));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void CreateDiscussion(CreatediscussionCommand command, Action<Discussion> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Discussion(s)));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void RetrieveDiscussionComments(RetrieveDiscussionCommentsCommand command, Action<CommentCollection> callback)
        {
            this.GetResponseBodyText(command, s => callback(new CommentCollection(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void UpdateDiscussion(UpdateDiscussionCommand command, Action<Discussion> callback)
        {
            this.GetResponseBodyText(command, s => callback(new Discussion(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetEvents(GetEventsCommand command, Action<EventCollection> callback)
        {
            this.GetResponseBodyText(command, s => callback(new EventCollection(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void UploadFile(UploadFileCommand command, Action<TemporaryFile> callback)
        {
            this.GetResponseBodyText(command, s => callback(new TemporaryFile(s)));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="command"></param>
        /// <param name="callback"></param>
        public void GetFileVersions(GetFileVersionsCommand command, Action<String> callback)
        {
            this.GetResponseBodyText(command, s => callback(s));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        public void GetRootFolder(Action<Folder> callback)
        {
            this.GetFolderInfo("0", callback);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        public void GetRootItems(Action<ItemCollection> callback)
        {
            this.RetrieveFolderItems("0", items => callback(items));
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="callback"></param>
        public void GetRootFolders(Action<List<Folder>> callback)
        {
            this.RetrieveFolderItems("0", items =>
            {
                var folders = this.GetFolders(items);
                callback(folders);
            });
        }
        private List<Folder> GetFolders(ItemCollection items)
        {
            var l = new List<Folder>();
            foreach (var item in items)
            {
                if (String.Equals(item.Type, "folder", StringComparison.OrdinalIgnoreCase) == true)
                {
                    l.Add(new Folder(item.JsonText));
                }
            }
            return l;
        }
    }
}
